﻿using NPOI.OpenXmlFormats.Wordprocessing;
using NPOI.XWPF.UserModel;
using System.Reflection;

namespace Common.Framework.Core.Npoi
{
    public class NpoiWordHelper
    {

        #region 操作word

        /// <summary>
        /// 复制表格
        /// </summary>
        /// <param name="fileWord">word对象</param>
        /// <param name="sourceIndex">原表格索引</param>
        /// <param name="targetIndex">目标位置索引</param>
        public static void CopyTable(XWPFDocument fileWord, int sourceIndex, int targetIndex)
        {
            var sourceTable = fileWord.Tables[sourceIndex];
            CT_Tbl sourceCTTBl = fileWord.Document.body.GetTblArray(1);

            var targetTable = fileWord.CreateTable();
            fileWord.SetTable(targetIndex, targetTable);
            var targetCTTbl = fileWord.Document.body.GetTblArray()[fileWord.Document.body.GetTblArray().Length - 1];

            targetCTTbl.tblPr = sourceCTTBl.tblPr;
            targetCTTbl.tblGrid = sourceCTTBl.tblGrid;

            for (int i = 0; i < sourceTable.Rows.Count; i++)
            {
                var tbRow = targetTable.CreateRow();
                var targetRow = tbRow.GetCTRow();
                tbRow.RemoveCell(0);
                XWPFTableRow row = sourceTable.Rows[i];
                targetRow.trPr = row.GetCTRow().trPr;
                //targetRow.trPr = row.GetCTRow().trPr
                //targetRow.trPr = row.GetCTRow().trPr
                //targetRow.trPr = row.GetCTRow().trPr
                for (int j = 0; j < row.GetTableCells().Count; j++)
                {
                    var tbCell = tbRow.CreateCell();
                    tbCell.RemoveParagraph(0);
                    var targetCell = tbCell.GetCTTc();
                    XWPFTableCell cell = row.GetTableCells()[j];
                    targetCell.tcPr = cell.GetCTTc().tcPr;
                    for (int z = 0; z < cell.Paragraphs.Count; z++)
                    {
                        var tbPhs = tbCell.AddParagraph();
                        XWPFParagraph para = cell.Paragraphs[z];
                        for (int y = 0; y < para.Runs.Count; y++)
                        {
                            var tbRun = tbPhs.CreateRun();
                            CT_R targetRun = tbRun.GetCTR();

                            XWPFRun run = para.Runs[y];
                            var runCTR = run.GetCTR();
                            targetRun.rPr = runCTR.rPr;
                            targetRun.rsidRPr = runCTR.rsidRPr;
                            targetRun.rsidR = runCTR.rsidR;
                            CT_Text text = targetRun.AddNewT();
                            text.Value = run.Text;
                        }
                    }
                }
            }
            targetTable.RemoveRow(0);
        }

        /// <summary>
        /// 合并行
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="dataTable">原始表格</param>
        /// <param name="tList">原始数据</param>
        /// <param name="eachCountFunc">循环次数count条件</param>
        /// <param name="mergeCountFunc">合并行数count条件</param>
        /// <param name="columnIndex">原始表格需要合并的列下标</param>
        /// <param name="firstMergeIndex">从哪一行开始合并,firstMergeIndex=0从下标为1的行开始合并;firstMergeIndex>=1则从firstMergeIndex+1的下标行开始合并</param>
        public static void MergeRow<T>(XWPFTable dataTable, List<T> tList, Func<T, object> eachCountFunc, Func<T, T, bool> mergeCountFunc, int columnIndex, int firstMergeIndex = 0)
        {
            Dictionary<int, int> mergeDic = new Dictionary<int, int>(); //key:开始行,value:需要合并的行数
            KeyValuePair<int, int> tmpMergeDic = new KeyValuePair<int, int>();
            T tInfo = default(T);
            int mergeCount = 0;

            int tcCount = tList.Select(eachCountFunc).Distinct().Count(); //需要合并的列 循环的次数
            for (int i = 1; i <= tcCount; i++)
            {
                if (mergeDic.Count > 0)
                {
                    //如果不是首次进入循环体,则通过下标方式从字典充获取对象
                    tmpMergeDic = mergeDic.ElementAt(i - 2);
                }

                if (tmpMergeDic.Value > 0)
                {
                    //如果 字典value大于0,说明拿到了值,需要获取新的原始数据对象
                    tInfo = tList[tmpMergeDic.Key + tmpMergeDic.Value - 1];
                }
                else
                {
                    //如果 字典value小于等于0 说明是首次进入循环体
                    tInfo = tList[i - 1];
                }
                //计算需要合并的行数
                mergeCount = tList.Count(s => mergeCountFunc(s, tInfo));
                if (mergeCount > 1)
                {
                    if (tmpMergeDic.Value > 0)
                    {
                        //如果不是首次进入循环体, 需要从集合充 拿到key和value
                        mergeDic.Add(tmpMergeDic.Key + tmpMergeDic.Value, mergeCount);
                    }
                    else
                    {
                        //如果是首次进入循环体,直接为 集合赋值
                        mergeDic.Add(i, mergeCount);
                    }
                }
                else
                {
                    //如果进入该分支,说明 记录只有一行,不需要合并行
                    if (tmpMergeDic.Key <= 0)
                    {
                        mergeDic.Add(1 + tmpMergeDic.Value, 1);
                    }
                    else
                    {
                        mergeDic.Add(tmpMergeDic.Key + tmpMergeDic.Value, 1);
                    }
                }
            }

            //foreach (var item in mergeDic)
            //{
            //    if (item.Value > 1)
            //    {
            //        MergeCell(dataTable, item.Key, item.Key + item.Value - 1, columnIndex);
            //    }
            //}

            for (int i = 0; i < mergeDic.Count; i++)
            {
                var dic = mergeDic.ElementAt(i);

                if (dic.Value > 1)
                {
                    if (firstMergeIndex == 0)
                    {
                        MergeCell(dataTable, dic.Key, dic.Key + dic.Value - 1, columnIndex);
                    }
                    else
                    {
                        MergeCell(dataTable, dic.Key + firstMergeIndex, firstMergeIndex + dic.Key + dic.Value - 1, columnIndex);
                    }
                }
            }
        }


        /// <summary>
        /// 合并单元格,且设置文本 上下左右居中
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="fromRow"></param>
        /// <param name="toRow"></param>
        /// <param name="col"></param>
        public static void MergeCell(XWPFTable dataTable, int fromRow, int toRow, int col)
        {
            for (int rowIndex = fromRow; rowIndex <= toRow; rowIndex++)
            {
                XWPFTableCell cell = dataTable.GetRow(rowIndex).GetCell(col);

                CT_Tc cttc = cell.GetCTTc();
                if (cttc.tcPr == null)
                {
                    cttc.AddNewTcPr();
                }

                //第一个合并单元格用重启合并值设置
                if (rowIndex == fromRow)
                {
                    cell.GetCTTc().AddNewTcPr().AddNewVMerge().val = ST_Merge.restart;
                }
                else
                {
                    //合并第一个单元格的单元被设置为“继续”
                    cell.GetCTTc().AddNewTcPr().AddNewVMerge().val = ST_Merge.@continue;
                }
            }
            dataTable.GetRow(fromRow).GetCell(col).SetVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
        }
        #endregion
        /// <summary>
        /// 写段落及格式
        /// </summary>
        /// <param name="document"></param>
        /// <param name="paragraphAlign">文字显示位置,段落排列(左对齐，居中，右对齐)</param>
        /// <param name="fillContent">内容</param>
        /// <param name="fontSize">设置文字大小</param>
        /// <param name="fontColor">设置字体颜色--十六进制</param>
        /// <param name="fontFamily">设置标题样式如：（微软雅黑，隶书，楷体）根据自己的需求而定</param>
        /// <param name="underline">设置下划线，枚举类型</param>
        /// <param name="textPosition">设置换行</param>
        /// <param name="vertical">设置下标，枚举类型</param>
        /// <param name="rowledge">行距单倍为默认值（240twip）不需设置，1.5倍=240X1.5=360twip，2倍=240X2=480twip</param>
        ///  <param name="isBold">文字加粗</param>
        /// <param name="isItalic">是否设置斜体（字体倾斜）</param>
        /// <param name="isDoubleStrike">是否显示双删除线</param>
        /// <param name="isImprinted">印迹（悬浮阴影）,效果和浮雕类似</param>
        /// <param name="isStrike">是否显示单删除线</param>
        /// <returns></returns>
        public void SetParagraph(XWPFDocument document, ParagraphAlignment paragraphAlign, string fillContent, int fontSize, string fontColor, string fontFamily, UnderlinePatterns underline, int textPosition, VerticalAlign vertical, string rowledge = "400", bool isBold = false, bool isItalic = false, bool isDoubleStrike = false, bool isImprinted = false, bool isStrike = false)
        {
            CT_P m_p = document.Document.body.AddNewP();
            //行距固定20磅
            m_p.AddNewPPr().AddNewSpacing().line = rowledge;
            m_p.AddNewPPr().AddNewSpacing().lineRule = ST_LineSpacingRule.exact;
            //创建段落对象
            XWPFParagraph paragraph = new XWPFParagraph(m_p, document);
            //文字显示位置,段落排列（左对齐，居中，右对齐）
            paragraph.Alignment = paragraphAlign;
            //创建段落文本对象
            XWPFRun xwpfRun = paragraph.CreateRun();
            //文字加粗
            xwpfRun.IsBold = isBold;
            xwpfRun.SetText(fillContent);
            //设置文字大小
            xwpfRun.FontSize = fontSize;
            //是否设置斜体（字体倾斜）
            xwpfRun.IsItalic = isItalic;
            //设置字体颜色--十六进制
            xwpfRun.SetColor(fontColor);
            //设置标题样式如：（微软雅黑，隶书，楷体）根据自己的需求而定
            xwpfRun.SetFontFamily(fontFamily, FontCharRange.None);
            //是否显示双删除线
            xwpfRun.IsDoubleStrikeThrough = isDoubleStrike;
            //是否显示单删除线
            xwpfRun.IsStrikeThrough = isStrike;
            //设置下划线，枚举类型
            xwpfRun.SetUnderline(underline);
            //添加回车键
            xwpfRun.AddCarriageReturn();
            //印迹（悬浮阴影）,效果和浮雕类似
            xwpfRun.IsImprinted = isImprinted;
            //设置下标，枚举类型
            xwpfRun.Subscript = vertical;
        }


        /// <summary>
        /// 设置单元格内容
        /// </summary>
        /// <param name="table"></param>
        /// <param name="rowIndex"></param>
        /// <param name="cellIndex"></param>
        /// <param name="content"></param>
        /// <param name="paragraph"></param>
        /// <param name="fontSize"></param>
        /// <param name="textAlignment"></param>
        public void SetTitleContentForFourthTable(XWPFTable table, int rowIndex, int cellIndex, string content, ParagraphAlignment paragraph,int fontSize, TextAlignment textAlignment)
        {
            var rowcell = table.GetRow(rowIndex).GetCell(cellIndex);
            rowcell.SetVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);
            var para = new CT_P();
            var pCell = new XWPFParagraph(para, table.Body);
            pCell.Alignment = paragraph;
            //字体位置
            pCell.VerticalAlignment = textAlignment; 
            var run = pCell.CreateRun();
            run.SetText(content);
            run.FontSize = fontSize;
            run.SetFontFamily("宋体", FontCharRange.None);
            //放入单元格
            table.GetRow(rowIndex).GetCell(cellIndex).SetParagraph(pCell);
        }


        /// <summary>
        /// 表格内容
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="doc"></param>
        /// <param name="keyValuePairs"></param>
        /// <param name="whichNumber"></param>
        /// <param name="isBorder"></param>
        /// <param name="tableData"></param>
        /// <param name="properties"></param>
        /// <param name="width"></param>
        /// <param name="fontSize"></param>
        /// <param name="textAlignment"></param>
        public void TableContent<T>(XWPFDocument doc, Dictionary<string, string> keyValuePairs, int whichNumber, bool isBorder, List<T> tableData, PropertyInfo[] properties, string width, int fontSize, TextAlignment textAlignment)
        {
            Type entityType = typeof(T);
            //默认两行来存放属性名和属性值，键值对中放入列名加属性名
            XWPFTable table = doc.CreateTable(2, keyValuePairs.Count);
            //获得文档第一张表
            CT_Tbl m_CTTbl = doc.Document.body.GetTblArray()[whichNumber];
            //表格水平居中
            m_CTTbl.AddNewTblPr().jc = new CT_Jc();
            m_CTTbl.AddNewTblPr().jc.val = ST_Jc.center;
            //表格宽度
            m_CTTbl.AddNewTblPr().AddNewTblW().w = width;//表宽度
            m_CTTbl.AddNewTblPr().AddNewTblW().type = ST_TblWidth.dxa;
            //列数
            int celCount = 0;
            foreach (var item in keyValuePairs)
            {
                SetTitleContentForFourthTable(table, 0, celCount, item.Key, ParagraphAlignment.CENTER, fontSize, textAlignment);
                SetTitleContentForFourthTable(table, 1, celCount, item.Value, ParagraphAlignment.CENTER, fontSize, textAlignment);
                celCount++;
            }
            //模板行
            XWPFTableRow dataTempRow = table.GetRow(1);
            CT_Row ctrow = dataTempRow.GetCTRow();
            if (tableData != null && tableData.Any())
            {
                //新增行
                for (int i = 0; i < tableData.Count; i++)
                {
                    CT_Row targetRow = new CT_Row();
                    //复制cell结构
                    CopyCell<T>(tableData[i], properties, ctrow, targetRow);

                    //增加数据行
                    XWPFTableRow mrow = new XWPFTableRow(targetRow, table);
                    table.AddRow(mrow);
                }
            }
            //删除模板行
            table.RemoveRow(table.Rows.IndexOf(dataTempRow));
            //创建段落
            doc.CreateParagraph();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <param name="typeProperties"></param>
        /// <param name="ctrow"></param>
        /// <param name="targetRow"></param>
        public static void CopyCell<T>(T data, PropertyInfo[] typeProperties, CT_Row ctrow, CT_Row targetRow)
        {
            foreach (CT_Tc item in ctrow.Items)
            {
                CT_Tc addTc = targetRow.AddNewTc();
                addTc.tcPr = item.tcPr;//cell样式,只包括列宽和cell对齐方式

                IList<CT_P> list_p = item.GetPList();

                foreach (var p in list_p)
                {
                    CT_P addP = addTc.AddNewP();
                    addP.pPr = p.pPr;//段落样式
                    IList<CT_R> list_r = p.GetRList();
                    foreach (CT_R r in list_r)
                    {
                        CT_R addR = addP.AddNewR();
                        addR.rPr = r.rPr;//run样式 包括字体等
                        List<CT_Text> list_text = r.GetTList();
                        foreach (CT_Text text in list_text)
                        {
                            CT_Text addText = addR.AddNewT();
                            addText.space = text.space;
                            addText.Value = ReplaceKeyForCell<T>(data, typeProperties, text.Value);
                        }
                    }
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="reportDto"></param>
        /// <param name="properties"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        private static string ReplaceKeyForCell<T>(T reportDto, PropertyInfo[] properties, string key)
        {
            string targetValue = "";
            var targetProp = properties.FirstOrDefault(s => s.Name == key);
            if (targetProp != null)
            {
                targetValue = Convert.ToString(targetProp.GetValue(reportDto));
            }
            return targetValue;
        }


        /// <summary>
        /// 隐藏表格边框线
        /// </summary>
        /// <param name="xWPFTable"></param>
        /// <param name="rowCount"></param>
        /// <param name="cellCount"></param>
        public void ConcealTableBorder(XWPFTable xWPFTable, int rowCount, int cellCount)
        {
            for (int i = 0; i < rowCount; i++)
            {
                for (int y = 0; y < cellCount; y++)
                {
                    XWPFTableCell cell = xWPFTable.GetRow(i).GetCell(y);
                    cell.SetBorderBottom(XWPFTable.XWPFBorderType.NIL, 0, 0, "white");
                    cell.SetBorderLeft(XWPFTable.XWPFBorderType.NIL, 0, 0, "white");
                    cell.SetBorderRight(XWPFTable.XWPFBorderType.NIL, 0, 0, "white");
                    cell.SetBorderTop(XWPFTable.XWPFBorderType.NIL, 0, 0, "white");
                }
            }
        }

        /// <summary>
        /// 报告加签名
        /// </summary>
        /// <param name="row"></param>
        /// <param name="cell"></param>
        /// <param name="stream"></param>
        /// <param name="table"></param>
        /// <param name="imgName"></param>
        public static void ReportAddSign(int row, int cell, Stream stream, XWPFTable table, string imgName)
        {
            XWPFTableCell Cell = table.GetRow(row).GetCell(cell);
            var gp = Cell.AddParagraph();
            var gr = gp.CreateRun();
            gr.AddPicture(stream, (int)NPOI.XWPF.UserModel.PictureType.PNG, imgName, 700000, 500000);
        }
        /// <summary>
        /// 替换
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="para"></param>
        /// <param name="reportDto"></param>
        /// <param name="properties"></param>
        public static void ReplaceKeyRow<T>(XWPFParagraph para, T reportDto, PropertyInfo[] properties)
        {
            var runs = para.Runs;
            string text = string.Empty;
            foreach (var run in runs)
            {
                text = run.ToString();
                if (!string.IsNullOrWhiteSpace(text))
                {
                    foreach (var prop in properties)
                    {
                        string propteryName = prop.Name;
                        string value = Convert.ToString(prop.GetValue(reportDto));
                        if (value == null)
                        {
                            value = "";
                        }
                        if (text.Contains(propteryName))
                        {
                            text = text.Replace(propteryName, value);
                            run.SetText(text);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// 遍历段落
        /// </summary>
        /// <param name="data"></param>
        /// <param name="autoGenerateReportDtoProperties"></param>
        /// <param name="doc"></param>
        public static void OperateEachParagraph<T>(T data, PropertyInfo[] autoGenerateReportDtoProperties, XWPFDocument doc)
        {
            foreach (var para in doc.Paragraphs)
            {
                ReplaceKeyRow<T>(para, data, autoGenerateReportDtoProperties);
            }
        }
        /// <summary>
        /// 遍历表格段落
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <param name="autoGenerateReportDtoProperties"></param>
        /// <param name="table"></param>
        public static void TraverseTableParagraphs<T>(T data, PropertyInfo[] autoGenerateReportDtoProperties, XWPFTable table)
        {
            foreach (var row in table.Rows)
            {
                foreach (var cell in row.GetTableCells())
                {
                    foreach (var para in cell.Paragraphs)
                    {
                        ReplaceKeyRow<T>(para, data, autoGenerateReportDtoProperties);
                    }
                }
            }
        }

        /// <summary>
        /// 表格模板行内容
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tableData"></param>
        /// <param name="properties"></param>
        /// <param name="table"></param>
        /// <param name="tempRow"></param>
        public static void TemplateRowContent<T>(List<T> tableData, PropertyInfo[] properties, XWPFTable table, int tempRow)
        {
            XWPFTableRow dataTempRow = table.GetRow(tempRow);
            CT_Row ctrow = dataTempRow.GetCTRow();
            if (tableData != null && tableData.Any())
            {
                for (int i = 0; i < tableData.Count; i++)
                {
                    CT_Row targetRow = new CT_Row();

                    //复制cell结构
                    CopyCell<T>(tableData[i], properties, ctrow, targetRow);

                    //增加数据行
                    XWPFTableRow mrow = new XWPFTableRow(targetRow, table);
                    table.AddRow(mrow);
                }
            }
            //删除模板行
            table.RemoveRow(table.Rows.IndexOf(dataTempRow));
        }


        /// <summary>
        /// 合并单元格
        /// </summary>
        /// <param name="table"></param>
        /// <param name="startColumn"></param>
        /// <param name="endColumn"></param>
        /// <param name="startRow"></param>
        /// <param name="endRow"></param>
        public static void MergeCellsCustom(XWPFTable table, int startColumn, int endColumn, int startRow, int endRow)
        {
            for (int rowIndex = startRow; rowIndex <= endRow; rowIndex++)
            {
                if (startColumn < endColumn)
                {
                    table.GetRow(rowIndex).MergeCells(startColumn, endColumn);
                }
                XWPFTableCell rowcell = table.GetRow(rowIndex).GetCell(startColumn);
                CT_Tc cTTC = rowcell.GetCTTc();
                if (cTTC.tcPr == null)
                {
                    cTTC.AddNewTcPr();
                }
                if (rowIndex == startRow)
                {
                    rowcell.GetCTTc().tcPr.AddNewVMerge().val = ST_Merge.restart;
                }
                else
                {
                    rowcell.GetCTTc().tcPr.AddNewVMerge().val = ST_Merge.@continue;
                }
            }
        }
    }
}
